package ru.pb.game.objects.model.items;

import java.sql.Connection;
import java.util.concurrent.Future;

import ru.pb.game.dao.ItemsDAO;
import ru.pb.game.data.templates.AbstractItemTemplate;
import ru.pb.game.data.xml.holders.ItemHolder;
import ru.pb.game.idfactory.IdFactory;
import ru.pb.game.network.threading.ThreadPoolManager;
import ru.pb.game.objects.model.GameObject;
import ru.pb.game.objects.model.inventory.IItemInfo;
import ru.pb.game.objects.model.items.enums.ItemLocation;
import ru.pb.game.objects.model.items.tasks.LazyUpdateInDb;
import ru.pb.game.objects.model.players.Player;
import ru.pb.game.objects.storage.ObjectsStorage;
import ru.pb.global.dao.DAOManager;
import ru.pb.global.data.model.ItemInfo;
import ru.pb.global.threading.RunnableTask;

public class Item extends GameObject implements IItemInfo, Cloneable
{
	private static final ItemsDAO _dao = DAOManager.getDAOImpl(ItemsDAO.class);

	private final AbstractItemTemplate _template;

	public static final byte EXISTS_IN_DATABASE = 1 << 0;
	public static final byte NEED_UPDATE = 1 << 1;
	public static final byte NOT_NEED_INSERT = 1 << 2;

	private Future<?> _lazyUpdateInDb;
	private byte _databaseFlags;
	private int _ownerObjectId;

	private ItemInfo _itemInfo;

	private LifeTimeTask _itemLifeTimeTask;

	private boolean _isEquiped;

	public Item(int objectId, AbstractItemTemplate template, int ownerObjectId, ItemInfo info)
	{
		super(objectId);

		_template = template;
		_ownerObjectId = ownerObjectId;

		_itemInfo = info;

		switch(ItemLocation.valueOf(info.getEquippedLocation()))
		{
			case PAPERDOLL: // Если item одета, то вызываем метод добавления в одетые итемы.
			{
				getOwner().getInventory().equipItem(this);
				break;
			}
		}
		activeDatabaseFlag(EXISTS_IN_DATABASE);
	}

	public Item(int objectId, int itemId)
	{
		this(objectId, ItemHolder.getInstance().getTemplate(itemId), new ItemInfo(itemId, ItemHolder.getInstance().getTemplate(itemId).getItemType().ordinal(), ItemHolder.getInstance().getTemplate(itemId).getItemSlot().ordinal()));
	}

	public Item(int objectId, AbstractItemTemplate template, ItemInfo itemInfo)
	{
		super(objectId);
		if(template == null)
			throw new IllegalArgumentException();
		_template = template;
		_itemInfo = itemInfo;
	}

	public ItemInfo getItemInfo()
	{
		return _itemInfo;
	}

	public AbstractItemTemplate getTemplate()
	{
		return _template;
	}

	public int getItemId()
	{
		return _template.getItemId();
	}

	public void setEquipped(boolean b)
	{
		_isEquiped = b;
	}

	public boolean isEquipable()
	{
		return getTemplate().isEquipable();
	}

	@Override
	public boolean isEquipped()
	{
		return _isEquiped;
	}

	public ItemLocation getLocation()
	{
		return ItemLocation.valueOf(_itemInfo.getEquippedLocation());
	}

	@Override
	public int getCount()
	{
		return _itemInfo.getCount();
	}

	public void setCount(int count)
	{
		_itemInfo.setCount(count);
	}

	@Override
	public boolean isStackable()
	{
		return _template.isStackable();
	}

	public boolean isActiveDatabaseFlag(byte b)
	{
		return (_databaseFlags & b) == b;
	}

	public void activeDatabaseFlag(byte b)
	{
		_databaseFlags |= b;
	}

	public void deactiveDatabaseFlag(byte b)
	{
		_databaseFlags &= ~b;
	}

	public void updateDatabase()
	{
		updateDatabase(null, false);
	}

	public synchronized void updateDatabase(Connection con, boolean commit)
	{
		if(isActiveDatabaseFlag(NOT_NEED_INSERT))
			return;

		if(isActiveDatabaseFlag(EXISTS_IN_DATABASE))
		{
			if(_ownerObjectId == 0 || getLocation() == ItemLocation.TOREMOVE || getCount() == 0)
				_dao.delete(con, this);
			else if( !isStackable())
				_dao.update(con, this);
			else
			{
				if(commit)
				{
					// cancel lazy update task if need
					if(stopLazyUpdateTask(true))
					{
						_dao.insert(con, this);
						return;
					}
					_dao.update(con, this);
					return;
				}
				Future<?> lazyUpdateInDb = _lazyUpdateInDb;
				if(lazyUpdateInDb == null || lazyUpdateInDb.isDone())
					_lazyUpdateInDb = ThreadPoolManager.getInstance().schedule(new LazyUpdateInDb(this), 60000);
			}
		}
		else
		{
			if(getCount() == 0)
				return;

			if(getLocation() == ItemLocation.TOREMOVE || _ownerObjectId == 0)
				return;

			_dao.insert(con, this);
		}
	}

	public boolean stopLazyUpdateTask(boolean interrupt)
	{
		boolean ret = false;
		if(_lazyUpdateInDb != null)
		{
			ret = _lazyUpdateInDb.cancel(interrupt);
			_lazyUpdateInDb = null;
		}
		return ret;
	}

	/**
	 * Returns the ownerID of the itemTemplate
	 * 
	 * @return int : ownerID of the itemTemplate
	 */
	public int getOwnerObjectId()
	{
		return _ownerObjectId;
	}

	/**
	 * Sets the ownerID of the itemTemplate
	 * 
	 * @param owner_id
	 *            : int designating the ID of the owner
	 */
	public void setOwnerObjectId(int owner_id)
	{
		if(owner_id == getOwnerObjectId())
			return;

		_ownerObjectId = owner_id;
		activeDatabaseFlag(NEED_UPDATE);
	}

	/**
	 * Sets the location of the itemTemplate.<BR><BR>
	 * <U><I>Remark :</I></U> If loc and loc_data different from database, say datas not up-to-date
	 * 
	 * @param loc
	 *            : ItemLocation (enumeration)
	 */
	public void setLocation(ItemLocation loc)
	{
		if(loc == getLocation())
			return;

		_itemInfo.setEquippedLocation(loc.ordinal());

		activeDatabaseFlag(NEED_UPDATE);
	}

	public void removeFromInventory()
	{
		setOwnerObjectId(0);
		setLocation(ItemLocation.TOREMOVE);
		updateDatabase(null, true);
	}

	public void fullDestroy()
	{
		setOwnerObjectId(0);
		setLocation(ItemLocation.TOREMOVE);
		setCount(0);
		deleteMe(true);
		updateDatabase(null, true);
	}

	@Override
	public Item clone()
	{
		return new Item(IdFactory.getInstance().getNextId(), _template, _itemInfo);
	}

	@Override
	public int getLifeTimeRemaining()
	{
		return _itemInfo.getLifeTime() - (int) (System.currentTimeMillis() / 1000);
	}

	public int getLifeTimeRemainingForDatabase()
	{
		return _itemInfo.getLifeTime();
	}

	private void setLifeTimeRemaining(Player owner, int lt)
	{
		assert !isTemporalItem();

		_itemInfo.setLifeTime(lt);

		activeDatabaseFlag(NEED_UPDATE);
	}

	public class LifeTimeTask extends RunnableTask
	{
		public void runImpl()
		{
			if(_itemLifeTimeTask != this)
				return;

			if( !isEquipped())
				return;

			Player owner = getOwner();
			if(owner == null || !owner.isOnline())
				return;

			if(isTemporalItem())
				setLifeTimeRemaining(owner, getLifeTimeRemaining());

			if(needsDestruction(owner))
				return;

			// ThreadPoolManager.getInstance().schedule(this, 60000); // У шэдовов 1 цикл = 60 сек.
		}
	}

	/**
	 * true означает завершить таск, false продолжить
	 */
	private boolean needsDestruction(Player owner)
	{
		if( !isTemporalItem())
			return true;

		int left = getLifeTimeRemaining();
		if(isTemporalItem())
			left /= 60;

		if(left == 10 || left == 5 || left == 1 || left <= 0)
		{
			if(left <= 0)
			{
				owner.getInventory().unEquipItem(this);
				owner.getInventory().destroyItem(this, getCount());
				return true;
			}
		}
		return false;
	}

	public Player getOwner()
	{
		return _ownerObjectId == 0 ? null : ObjectsStorage.getInstance().getPlayer(_ownerObjectId);
	}

	@Override
	public boolean isTemporalItem()
	{
		return _template.isTemporal();
	}

	public int getFlags()
	{
		return _itemInfo.getFlags();
	}
}